/* 
 * File:   svg_polygon_path.cpp
 * Author: Satan
 * 
 * Created on September 12, 2012, 9:19 PM
 */

#include "svgloader/svg_polygon_path.h"
#include "svgloader/svg_path_move.h"
#include "svgloader/svg_path_line.h"
#include "svgloader/svg_path_curve.h"
#include "hfgl/hfgl_polygon.h"
#include "common.h"
#include "engine.h"

svg_polygon_path::svg_polygon_path(TiXmlElement* child) : svg_polygon(child)
{
    std::string lineD = common::xml_getAttribute(child, "d");
    
    int pos = 0;
    float *params = NULL;
    int paramsCount = 0;
    int instType = TYPE_INSTRUCT_NONE;
    float last_x = 0.0;
    float last_y = 0.0;
    
    while (pos < lineD.length())
    {
        // Пока не уткнусь в допустимую инструкцию, перебираю символы
        while ((lineD[pos] != 'M' && lineD[pos] != 'L' && lineD[pos] != 'C') && pos < lineD.length())
            pos++;
        
        if (pos >= lineD.length())
            continue;
        
        // Определяю тип инструкции и количество параметров полагаемых для неё
        switch (lineD[pos])
        {
            case 'M':
                instType = TYPE_INSTRUCT_MOVE;
                paramsCount = 2;
                break;

            case 'L':
                instType = TYPE_INSTRUCT_LINE;
                paramsCount = 2;
                break;

            case 'C':
                instType = TYPE_INSTRUCT_CURVE;
                paramsCount = 6;
                break;
                
            default:
                instType = TYPE_INSTRUCT_NONE;
                continue;
        };
        pos++;
        
        // Считываю параметры
        params = new float[paramsCount];
        for(int i = 0; i < paramsCount; i++)
            params[i] = getFloat(&lineD, &pos);
        
        // Создаю объект исходя из его типа
        switch(instType)
        {
            case TYPE_INSTRUCT_MOVE:
                m_instructions.push_back(new svg_path_instruction_move(params[0], params[1]));
                break;
            case TYPE_INSTRUCT_LINE:
                m_instructions.push_back(new svg_path_instruction_line(params[0], params[1]));
                break;
            case TYPE_INSTRUCT_CURVE:
                m_instructions.push_back(new svg_path_instruction_curve(last_x, last_y, params[0], params[1], params[2], params[3], params[4], params[5]));
                break;
        };

        // Сохраняю последнюю координату, ибо она требуются для некоторых инструкций
        if (paramsCount >= 2)
        {
            last_x = params[paramsCount-2];
            last_y = params[paramsCount-1];
        };        
        
        delete[] params;
        params = NULL;
    };  
}

svg_polygon_path::svg_polygon_path(const svg_polygon_path& orig) : svg_polygon(orig)
{
}

svg_polygon_path::~svg_polygon_path() 
{
    for(std::vector<svg_path_instruction*>::iterator it = m_instructions.begin(); it != m_instructions.end(); it++)
    {
        delete (*it);
        (*it) = NULL;
    }
}

float svg_polygon_path::getFloat(std::string* line, int* pos)
{
    int from = 0;
    while ((*line)[*pos] == ' ' && (*pos) < line->length())
        (*pos)++;
    from = *pos;
    while ((*line)[*pos] != ' ' && (*pos) < line->length())
        (*pos)++;    
    
    return atof(line->substr(from, *pos).c_str());
}


hfgl_surface* svg_polygon_path::getSurface(int step)
{
    hfgl_surface* ret = NULL;
    std::vector<hfgl_point>* surface = new std::vector<hfgl_point>();
    
    // Получаю многоугольник выполняя по очереди все инструкции
    for(std::vector<svg_path_instruction*>::iterator it =  m_instructions.begin(); it != m_instructions.end(); it++)
        (*it)->getPoints(surface, step);    
    
    // Удаление последней точки если она совпадает с первой
    while (surface->at(0).x == surface->at(surface->size()-1).x && surface->at(0).y == surface->at(surface->size()-1).y)
        surface->pop_back();
    
    // Проверяю его на выпуклость.
    if(isPolyConvex(surface))
    {
        hfgl_polygon* polygon = new hfgl_polygon(GL_POLYGON, surface, m_colorR, m_colorG, m_colorB);
        ret = new hfgl_surface(1, &polygon);
    } else
    {
        // Тесселирую 
        std::vector<PolnayaHueta>* tessSurface = common::tesselation(surface);
        
        hfgl_polygon **polygons = new hfgl_polygon*[tessSurface->size()];
        int i = 0;
        for(std::vector<PolnayaHueta>::iterator it =  tessSurface->begin(); it != tessSurface->end(); it++)
        {
            hfgl_polygon* polygon = new hfgl_polygon((*it).type, &(*it).hueta, m_colorR, m_colorG, m_colorB);
            polygons[i] = polygon;
            i++;
        };
        
        ret = new hfgl_surface(i, polygons);
        
        delete tessSurface;
    };
    delete surface;
    
    return ret;
}

bool svg_polygon_path::isPolyConvex(std::vector<hfgl_point>* points)
{
    // треугольник - всегда выпуклый.
    if (points->size() <= 3) 
        return true;
    int np = points->size();
    int res = sign(classify_point2D(points->at(0), points->at(1), points->at(2)));
    for (int i = 1; i < np - 2; i++)
        if (res != sign(classify_point2D(points->at(i), points->at(i+1), points->at(i+2))))
            return false;
    return true;
}

inline GLfloat svg_polygon_path::classify_point2D(hfgl_point p0, hfgl_point p1, hfgl_point p2) 
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) *( p1.y - p0.y);
}
inline int svg_polygon_path::sign(GLfloat a)
{
    return a < 0 ? -1 : 1;
}